package com.qf.iprate.filter;

import com.qf.iprate.utils.Constants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class IpRateLimitFilter implements Filter {

    @Autowired
    RedisTemplate redisTemplate;

    /**
     * 每个IP地址在3秒中之内最多发送10个请求，超过该数量给出错误提示:访问过于频繁
     * @param request  The request to process
     * @param response The response associated with the request
     * @param chain    Provides access to the next filter in the chain for this
     *                 filter to pass the request and response to for further
     *                 processing
     *
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.info("请求进入过滤器了，请求的ip地址为:{}",request.getRemoteAddr());

        Integer timewindow = 30000; // 时间间隔，单位毫秒
        Integer max_quest_count = 10; // 最大访问量
        Integer max_error_count = 10; // 频繁访问次数超过该值，进入黑名单

        String ip = request.getRemoteAddr();

        String key = Constants.IP_RATE_COUNT +ip;
        Long count = redisTemplate.opsForValue().increment(key);

        if (count==1){
            // 该ip第一次发送请求,设置key 的存活时间，代表统计的时间窗口
            redisTemplate.expire(key,timewindow, TimeUnit.MILLISECONDS);
            log.info("该ip第一次请求，直接放行,ip为:{}",ip);
            chain.doFilter(request,response);
        }else if (count<max_quest_count){
            log.info("该ip没有超过限流次数,ip为:{},目前累计访问次数为{}",ip,count);
            // 放行该请求
            chain.doFilter(request,response);
        }else {
            log.info("该ip访问过于频繁了ip为:{}",ip);

            // 统计该ip频繁访问的次数
            String hashkey = "ip:rateError";
            Long errorCount = redisTemplate.opsForHash().increment(hashkey, ip, 1);
            if (errorCount>max_error_count){
                // 加入黑名单
                log.info("该ip已经被加入黑名单了",ip);
                redisTemplate.opsForSet().add(Constants.IP_BLACKLIST_KEY,ip);
            }

            // 访问过于频繁了，返回错误信息
            response.getWriter().println("ip request too many times");
            return;
        }

    }
}
